﻿using AutoMapper;
using Inovout;
using Inovout.Data.Service;
using Inovout.Descriptor;
using KuaJing.Common.Domain;
using KuaJing.Customs.Domain;
using KuaJing.Orders.Domain;
using LinqToExcel;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace KuaJing.Orders.Service
{
    public class OrderService : RepositoryService<Order>
    {
        static OrderService()
        {
            Mapper.Initialize(cfg =>
            {
                cfg.CreateMap<IEcelOrder, Order>();
                cfg.CreateMap<Declaration, Order>()
                    .ForMember(d => d.Code, o => o.MapFrom(s => s.ContractNumber))
                    .ForMember(d => d.ExportDate, o => o.MapFrom(s => s.ExportDate))
                   .ForMember(d => d.DeclarationAmount, o => o.MapFrom(s => s.Items.Sum(item => item.GoodsAmount)))
                   .ForMember(d => d.DeclarationCurrency, o => o.MapFrom(s => s.Items.Any() ? s.Items.First().CurrencyName : null))
                   .ForMember(d => d.DeclarationCode, o => o.MapFrom(s => s.Code))
                   .ForMember(d => d.Provider, o => o.MapFrom(s => GetProvider(s.ContractNumber)))
                .ForAllOtherMembers(opt => opt.Ignore());
            });
        }
        private ProductService productService;
        private IRepositoryService<ExchangeRate> exchangeRateService;
        private IRepositoryService<Declaration> declarationService;
        private IRepositoryService<DrawbackOrderItem> drawbackOrderItemService;
        private IRepositoryService<FinancingOrderItem> financingOrderItemService;
        private IRepositoryService<Common.Domain.File> fileService;
        private CustomerService customerService;
        private static FieldDescriptor CreatedTimeField = TypeDescriptor.Get<Order>().GetField("createdTime");
        public OrderService(ProductService productService,
            IRepositoryService<DrawbackOrderItem> drawbackOrderItemService,
            IRepositoryService<FinancingOrderItem> financingOrderItemService,
             IRepositoryService<Common.Domain.File> fileService,
             IRepositoryService<Declaration> declarationService,
             CustomerService customerService, IRepositoryService<ExchangeRate> exchangeRateService
            )
        {
            this.productService = productService;
            this.drawbackOrderItemService = drawbackOrderItemService;
            this.financingOrderItemService = financingOrderItemService;
            this.fileService = fileService;
            this.customerService = customerService;
            this.exchangeRateService = exchangeRateService;
            this.declarationService = declarationService;
        }
        private List<Order> BuildOrders(IEnumerable<Row> worksheet)
        {
            var orders = new List<Order>();
            foreach (var row in worksheet)
            {
                string orderNumber = row[0].ToString().Trim();
                if (string.IsNullOrEmpty(orderNumber))
                {
                    continue;
                }
                var order = new Order();

                order.Code = orderNumber;
                order.Provider = GetProvider(orderNumber);
                order.CustomerName = row[1].ToString().Trim();
                // order.Customer = GetCustomer(order.CustomerName);
                order.OrderTime = Convert.ToDateTime(row[2].ToString().Trim());
                order.ExportDate = string.IsNullOrEmpty(row[4].ToString()) ? default(DateTime?) : Convert.ToDateTime(row[4].ToString().Trim());
                order.DeclarationCurrency = row[9].ToString().Trim();
                order.DeclarationAmount = decimal.Parse(row[10].ToString().Trim());
                order.AccountExchangeRate = decimal.Parse(row[11].ToString().Trim());

                order.TradeAmount = Math.Round(order.DeclarationAmount * order.AccountExchangeRate.Value, 2);
                order.Status = GetStatus(row[12].ToString().Trim());

                #region 退税
                Product drawbackProdcut = productService.GetProduct(row[13].ToString().Trim());

                DrawbackOrderItem drawbackOrderItem = new DrawbackOrderItem
                {
                    Order = order,
                    Product = drawbackProdcut,
                    Rate = row[14].Cast<decimal>()
                };
                order.Items.Add(drawbackOrderItem);
                #endregion

                #region 金融

                if (row[15].ToString().Trim() == "是")
                {
                    var financingProdcut = productService.GetProduct("赊销易");

                    FinancingOrderItem financingOrderItem = new FinancingOrderItem
                    {
                        Order = order,
                        Product = financingProdcut,

                        Currency = row[16].ToString().Trim(),
                        Amount = decimal.Parse(row[17].ToString().Trim()),
                        CnyAmount = decimal.Parse(row[18].ToString().Trim()),
                        Rate = decimal.Parse(row[19].ToString().Trim())

                    };
                    order.Items.Add(financingOrderItem);
                }
                #endregion
                orders.Add(order);
            }
            return orders;
        }

        private List<Order> BuildSimpleOrders(IEnumerable<Row> worksheet)
        {
            var orders = new List<Order>();
            foreach (var row in worksheet)
            {
                string orderNumber = row[20].ToString().Trim();
                if (string.IsNullOrEmpty(orderNumber) || string.IsNullOrEmpty(row[16].ToString().Trim()))
                {
                    continue;
                }
                var order = new Order();
                order.Code = orderNumber;
                order.Provider = GetProvider(orderNumber);
                order.CustomerName = row[0].ToString().Trim();
                // order.Customer = GetCustomer(order.CustomerName);
                //order.OrderTime = Convert.ToDateTime(row[16].ToString().Trim());
                order.ExportDate = string.IsNullOrEmpty(row[16].ToString()) ? default(DateTime?) : Convert.ToDateTime(row[16].ToString().Trim());
                order.DeclarationCode = row[21].ToString().Trim();
                order.DeclarationCurrency = row[26].ToString().Trim();
                order.DeclarationAmount = decimal.Parse(row[25].ToString().Trim());
                //order.AccountExchangeRate = decimal.Parse(row[11].ToString().Trim());

                order.TradeAmount = decimal.Parse(row[13].ToString().Trim()) * 10000; // Math.Round(order.DeclarationAmount * order.AccountExchangeRate, 2);
                order.ReportDate = Convert.ToDateTime(row[16].ToString().Trim());

                CreatedTimeField.SetValue(order, order.ReportDate);
                orders.Add(order);
            }
            return orders;
        }
        private static readonly ConcurrentDictionary<string, string> OrderCodeAndCustomerNameCache = new ConcurrentDictionary<string, string>();
        public IEnumerable<Order> Import(string fileName)
        {
            List<Order> orders = null;
            using (var excel = new ExcelQueryFactory(fileName))
            {
                if (excel.GetWorksheetNames().Contains("总计"))
                {
                    orders = BuildSimpleOrders(excel.Worksheet("总计"));
                    //orders = BuildSimpleOrders(excel.Worksheet("总计").Where(row => row["上报时间"].ToString() != string.Empty));
                    customerService.Import(fileName);
                }
                else if (excel.GetWorksheetNames().Contains("跨境总表"))
                {
                    orders = Query().Where(o => o.Customer == null).ToList();
                    foreach (var sheetName in new string[] { "跨境总表", "贸互通总表" })
                    {
                        foreach (var row in excel.Worksheet(sheetName))
                        {
                            string orderCode = row[0].ToString().Trim();
                            string customerName = row[1].ToString().Trim();
                            OrderCodeAndCustomerNameCache[orderCode] = customerName;
                            var order = orders.SingleOrDefault(o => o.Code == orderCode && o.Customer == null);
                            if (order != null)
                            {
                                order.CustomerName = customerName;
                            }
                        }
                        var file = fileService.Query().SingleOrDefault(f => f.Type == OrderCodeAndCustomerNameExcel);
                        if (file == null)
                        {
                            fileService.Save(new File { Name = fileName, Type = OrderCodeAndCustomerNameExcel });
                        }
                    }
                }
                else
                {
                    orders = BuildOrders(excel.Worksheet(0));
                }
            }

            var dbOrders = FindAll().ToArray();


            if (orders.Any(o => o.AccountExchangeRate.HasValue))
            {
                foreach (var dbOrder in dbOrders.Intersect(orders))
                {
                    var order = orders.Single(c => c.Code == dbOrder.Code);
                    if (!StructuralEqualityComparer<IEcelOrder>.Equals(order, dbOrder))
                    {
                        Mapper.Map<IEcelOrder, Order>(order, dbOrder);
                        orders[orders.IndexOf(order)] = dbOrder;
                        Update(dbOrder);
                    }
                }
            }
            else
            {
                foreach (var insertedOrder in orders.Except(dbOrders).Select(o =>
                {
                    o.Customer = GetCustomer(o.CustomerName);
                    return o;
                }).ToArray())
                {

                    Save(insertedOrder);
                    insertedOrder.Customer.Orders.Add(insertedOrder);
                    if (insertedOrder.Customer.FirstOrder == null)
                    {
                        insertedOrder.Customer.FirstOrder = insertedOrder;
                        customerService.Save(insertedOrder.Customer);
                    }
                }
                if (orders.Any(o => o.ReportDate.HasValue))
                {
                    foreach (var dbOrder in dbOrders.Intersect(orders.Where(o => !o.ReportDate.HasValue), new SimpleComparer()))
                    {
                        var order = orders.Single(c => c.Code == dbOrder.Code);
                        if (!StructuralEqualityComparer<IEcelOrder>.Equals(order, dbOrder))
                        {
                            Mapper.Map<IEcelOrder, Order>(order, dbOrder);
                            orders[orders.IndexOf(order)] = dbOrder;
                            Update(dbOrder);
                        }
                    }

                }
            }

            return orders;
        }
        class SimpleComparer : IEqualityComparer<Order>
        {
            public bool Equals(Order x, Order y)
            {
                return x.Code == y.Code && x.ReportDate == y.ReportDate;
            }

            public int GetHashCode(Order order)
            {
                return order.Code.GetHashCode() ^ (order.ReportDate.HasValue ? order.ReportDate.GetHashCode() : 0);
            }

        }
        private const string OrderCodeAndCustomerNameExcel = "OrderCodeAndCustomerNameExcel";
        private Customer GetCustomer(string customerName)
        {
            var customer = this.customerService.Query().SingleOrDefault(c => c.Name == customerName || c.ShortName == customerName);
            if (customer == null)
            {
                customer = new Customer(customerName) { ShortName = customerName };
                customerService.Save(customer);
            }
            return customer;
        }

        private OrderStatus GetStatus(string orderStatus)
        {
            switch (orderStatus)
            {
                case "已完成":
                case "已结案":
                    return OrderStatus.Executed;
                case "已取消":
                    return OrderStatus.Canceled;
                case "执行中":
                    return OrderStatus.Executing;
                default:
                    return OrderStatus.Pending;
            }

        }
        private static string GetProvider(string orderNumber)
        {
            if (string.IsNullOrEmpty(orderNumber))
            {
                return string.Empty;
            }
            switch (orderNumber.Substring(0, 3).ToUpper())
            {
                case "JKJ":
                    return "江苏跨境";
                case "MHT":
                    return "南京贸互通";
                default:
                    return "未知";

            }
        }
        private IDictionary<string, string> GetOrderCodeAndCustomerNameCache()
        {
            if (OrderCodeAndCustomerNameCache.Count == 0)
            {
                var file = fileService.Query().SingleOrDefault(f => f.Type == OrderCodeAndCustomerNameExcel);
                if (file != null)
                {
                    Import(file.Name);
                }
            }
            return OrderCodeAndCustomerNameCache;
        }
        public IEnumerable<Order> Relation()
        {
            List<Order> orders = new List<Order>();
            //报关单没有匹配订单，生成订单
            foreach (var declaration in declarationService.Query())
            {
                Order order;
                if (!TryGet(declaration, out order))
                {
                    order = Generate(declaration);
                }
                orders.Add(order);
            }
            return orders;
        }
        private void Relation(Order order, Declaration declaration)
        {
            if (order.Declarations.Contains(declaration))
            {
                throw new Exception("已关联！");
            }
            Mapper.Map<Declaration, Order>(declaration, order);
            if (order.Customer == null && GetOrderCodeAndCustomerNameCache().Keys.Contains(order.Code))
            {
                order.CustomerName = GetOrderCodeAndCustomerNameCache()[order.Code];
                order.Customer = GetCustomer(order.CustomerName);
            }
            order.Declarations.Add(declaration);
            Save(order);

        }
        public Order Generate(Declaration declaration)
        {
            if (Query().Any(o => o.Declarations.Contains(declaration) || o.Code == declaration.ContractNumber))
            {
                throw new Exception("报关单对应的订单已经存在！");
            }
            var exchangeRate = exchangeRateService.Query().Single(er =>
                er.PublishTime == new DateTimeRange(declaration.ExportDate, DateTimeRangeKind.Month).Start && er.Currency ==
                (Currency)Enum.Parse(typeof(Currency), declaration.Items.First().Currency));
            var order = new Order();
            Mapper.Map<Declaration, Order>(declaration, order);
            order.TradeAmount = Math.Round(order.DeclarationAmount * exchangeRate.BidRate, 2);
            Relation(order, declaration);

            return order;
        }

        public bool TryGet(Declaration declaration, out Order order)
        {
            order = Query().SingleOrDefault(o => o.Declarations.Contains(declaration) || o.Code == declaration.ContractNumber);
            if (order == null)
            {
                return false;
            }
            if (!order.Declarations.Contains(declaration))
            {
                Relation(order, declaration);
            }
            return true;
        }
    }
}
